package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.font.BitmapText;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.AmbientLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.CameraNode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.CameraControl;
import com.jme3.scene.shape.Box;
import com.jme3.texture.Texture;

/**
 * test
 * @author normenhansen
 */
public class Main extends SimpleApplication {
    private BulletAppState bulletAppState;
    private RigidBodyControl ball_phy;
    private Ball cameraBall;
    private Ball rollingBall;
    private float currentBallSpeed;
    BitmapText hudText;

//private RigidBodyControl floor_phy;

    public static void main(String[] args) {
        Main app = new Main();
        app.start();
    }

    @Override
    public void simpleInitApp() {
        
        currentBallSpeed = 0;
        initKeys(); //initkeys
        creatHudtext();
        //this code is for the physics engine
        bulletAppState = new BulletAppState();
        stateManager.attach(bulletAppState);
        
        
        /** A white ambient light source. */ 
        AmbientLight ambient = new AmbientLight();
        ambient.setColor(ColorRGBA.White);
        rootNode.addLight(ambient); 
        
        
        //This code creates the floor, and attaches it's Geeometry to the rootNode
        //rootNode.attachChild(makeFloor());
        
        //This code creates a Ball, and attaches it's Geeometry to the rootNode
        rollingBall = new Ball (assetManager,1);
        rollingBall.setLocalTranslation(0, 10, 0);
        rootNode.attachChild(rollingBall);
        createBallPhysics();
        
        Bottle bottle = new Bottle (assetManager,1,ColorRGBA.White,false,true);
        rootNode.attachChild(bottle);
        bottle.setLocalTranslation(0, 0, 5);
        RigidBodyControl bottle_phy = new RigidBodyControl(10);
        bottle.addControl(bottle_phy);
        bulletAppState.getPhysicsSpace().add(bottle_phy);
        
        
        
        // Disable the default flyby cam 
        flyCam.setEnabled(false);
        //ball_phy.setLinearVelocity(cam.getDirection().mult(25));
        
        
        cameraBall = new Ball(assetManager,0.0000000000000001f);
        rootNode.attachChild(cameraBall);
        
        initCamera();
        createSuper64World();

        
    }

    @Override
    public void simpleUpdate(float tpf) {
        //TODO: add update code
        
        cameraBall.setLocalTranslation(rollingBall.getLocalTranslation().getX(),rollingBall.getLocalTranslation().getY()+3,rollingBall.getLocalTranslation().getZ()); 
        hudText.setText("Rotate X: " + rollingBall.getLocalRotation().getX() + " Rotate Y: " + rollingBall.getLocalRotation().getY() + " Rotate Z: " + rollingBall.getLocalRotation().getZ());
        decelerate(tpf);
        //if (rollingBall.getLocalRotation().getY() > 0.5f || rollingBall.getLocalRotation().getY() < -0.5f ) {
            //cameraBall.rotate(0,(rollingBall.getLocalRotation().getY()/250), 0);
        
        
    }

    @Override
    public void simpleRender(RenderManager rm) {
        //TODO: add render code
    }
    
    /**
     * this method initializes the keybinding
     */
    public void initKeys()
    {
        /** Map one or more inputs to an action */
        inputManager.addMapping("Accelrate", new KeyTrigger(KeyInput.KEY_UP));
        inputManager.addMapping("Breaks", new KeyTrigger(KeyInput.KEY_DOWN));
        inputManager.addMapping("Turn left", new KeyTrigger(KeyInput.KEY_LEFT));
        inputManager.addMapping("Turn right", new KeyTrigger(KeyInput.KEY_RIGHT));

        /** Add an action to one or more listeners */
        inputManager.addListener(analogListener, "Accelrate");
        inputManager.addListener(analogListener, "Breaks"); 
        inputManager.addListener(analogListener, "Turn left");
        inputManager.addListener(analogListener, "Turn right"); 
    }
    /**
     * this method runs certain code depending on what buttons has been pressed
     */
    private AnalogListener analogListener = new AnalogListener()
    {
        public void onAnalog(String name, float value, float tpf)
        {
            if (name.equals("Accelrate"))
            {
                ball_phy.setLinearVelocity(cam.getDirection().mult(accelrate(tpf)));
            }
            if (name.equals("Breaks"))
            {
                ball_phy.setLinearVelocity(cam.getDirection().mult(breaks(tpf)));
            }
            if (name.equals("Turn left"))
            {                
                cameraBall.rotate(0, (FastMath.HALF_PI)*tpf, 0);
                ball_phy.setLinearVelocity(cam.getDirection().mult(decelerate(tpf)));
            }
            if (name.equals("Turn right"))
            {
                cameraBall.rotate(0, -(FastMath.HALF_PI)*tpf, 0);
                ball_phy.setLinearVelocity(cam.getDirection().mult(decelerate(tpf)));
            }
        }
    };
    float accelrate (float tpf)
    {
        if (currentBallSpeed < 100) {
            currentBallSpeed += 4f*tpf;
        } 
        else
        {
            currentBallSpeed = 100;
        }
       return currentBallSpeed;
    }
    float breaks (float tpf)
    {
        if (currentBallSpeed > 0) 
        {
        currentBallSpeed -= 4f*tpf;
        }
        else 
        {
            currentBallSpeed -= 2.75f*tpf;
        }
                return currentBallSpeed;
    }
    float decelerate (float tpf)
    {
        if (currentBallSpeed > 0.01f) 
        {
        currentBallSpeed -= 1.75f*tpf;
        }
        else if (currentBallSpeed < -0.01f) 
        {
            currentBallSpeed += 1.75f*tpf;
        }
        else    
        {
            currentBallSpeed = 0;
        }
        return currentBallSpeed;
    }
    /**
     * This method creates the floor
     * @return it returns the floor Geometry
     */
    protected Geometry makeFloor() 
    {
        Box box = new Box(500, .2f, 500);
        Geometry floor = new Geometry("the Floor", box);
        floor.setLocalTranslation(0, -2.75f, -5);
        Material mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        Texture cube1Tex = assetManager.loadTexture("Textures/grass.jpg");
        //mat1.setTexture("ColorMap", this.assetManager.loadTexture(new TextureKey("Textures/Grass.jpg", false)));
        mat1.setTexture("ColorMap", cube1Tex);
        floor.setMaterial(mat1);
        /* Make the floor physical with mass 0.0f! */
        RigidBodyControl floor_phy = new RigidBodyControl(0.0f);
        floor.addControl(floor_phy);
        bulletAppState.getPhysicsSpace().add(floor_phy);
        
        return floor;
    }
    protected void createBallPhysics()
    {
        /** Make the ball physcial with a mass > 0.0f */
        ball_phy = new RigidBodyControl(1f);
        /** Add physical ball to physics space. */
        rollingBall.addControl(ball_phy);
        bulletAppState.getPhysicsSpace().add(ball_phy);

    }
    void creatHudtext()
    {
        hudText = new BitmapText(guiFont, false);          
        hudText.setSize(guiFont.getCharSet().getRenderedSize());      // font size
        hudText.setColor(ColorRGBA.Blue);                             // font color
        hudText.setText("Test");             // the text
        hudText.setLocalTranslation(300, hudText.getLineHeight(), 0); // position
        guiNode.attachChild(hudText);
    }
    void initCamera()
    {
                //create the camera Node
        CameraNode camNode = new CameraNode("Camera Node", cam);
        //This mode means that camera copies the movements of the target:
        camNode.setControlDir(CameraControl.ControlDirection.SpatialToCamera);
        //Attach the camNode to the target:
        cameraBall.attachChild(camNode);
        //Move camNode, e.g. behind and above the target:
        camNode.setLocalTranslation(new Vector3f(0, 0, 10));
        //Rotate the camNode to look at the target:
        camNode.lookAt(cameraBall.getLocalTranslation(), Vector3f.UNIT_Y);
    }
    void createSuper64World()
    {
        Spatial teapot = assetManager.loadModel("Scenes/Mario64.j3o");
        Material mat_default = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
        teapot.setMaterial(mat_default);
        teapot.setLocalTranslation(0,-35,0);
        rootNode.attachChild(teapot);
        /* Make the floor physical with mass 0.0f! */
        RigidBodyControl teapot_phy = new RigidBodyControl(0.0f);
        teapot.addControl(teapot_phy);
        bulletAppState.getPhysicsSpace().add(teapot_phy);
        
    }
}
